package cn.dansj.common.utils.spring.validation;

import cn.dansj.common.processor.meta.WriteToAutoConfiguration;
import cn.dansj.common.utils.spring.advisor.AbstractBeanMethodFactoryPointcutAdvisor;
import cn.dansj.common.utils.enums.ResponseCode;
import cn.dansj.common.utils.lang.NotNull;
import cn.dansj.common.utils.spring.request.RequestUtils;
import cn.dansj.common.utils.spring.AnnotationUtils;
import cn.dansj.common.utils.spring.SpringGlobalEnv;
import cn.dansj.common.utils.spring.context.condition.ConditionalOnClass;
import cn.dansj.common.utils.transfer.ArrayUtils;
import cn.dansj.common.utils.transfer.Transformation;
import cn.dansj.common.utils.transfer.Verification;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Controller;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.stream.Collectors;

@WriteToAutoConfiguration
@ConditionalOnClass({Controller.class, MethodInvocation.class})
@Order(Ordered.HIGHEST_PRECEDENCE)
public class NotNullValidation extends AbstractBeanMethodFactoryPointcutAdvisor {
    @Override
    public boolean match(Method method, Class<?> targetClass) {
        return AnnotationUtils.hasAnnotation(targetClass, Controller.class) && Arrays.stream(method.getParameters()).anyMatch(parameter -> AnnotationUtils.hasAnnotation(parameter, NotNull.class));
    }

    @Override
    public Object execute(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod();
        Parameter[] parameters = method.getParameters();
        for (Parameter parameter : Arrays.stream(parameters).filter((parameter) -> AnnotationUtils.hasAnnotation(parameter, NotNull.class)).collect(Collectors.toList())) {
            int index = ArrayUtils.asList(parameters).indexOf(parameter);
            Object value = invocation.getArguments()[index];
            if (Verification.checkNull(value)) {
                RequestUtils.response(ResponseCode.ParameterNotPresent, parameter.getType().getSimpleName(), Transformation.requireNonNullElseGet(SpringGlobalEnv.NAME_DISCOVERER.getParameterNames(method), () -> Arrays.stream(parameters).map(Parameter::getName).toArray(String[]::new))[index]);
            }
        }
        return invocation.proceed();
    }
}
